package com.samphanie.auiu.auth.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.samphanie.auiu.auth.feign.IThirdPartyService;
import com.samphanie.auiu.auth.service.IRoleService;
import com.samphanie.auiu.auth.service.IUserAdminService;
import com.samphanie.auiu.auth.service.IUserRoleService;
import com.samphanie.auiu.auth.utils.CryptoUtil;
import com.samphanie.auiu.auth.utils.TotpUtil;
import com.samphanie.auiu.auth.utils.UserUtil;
import com.samphanie.auiu.common.dto.CreateOrUpdateClientDto;
import com.samphanie.auiu.common.dto.CreateUserDto;
import com.samphanie.auiu.common.dto.SendEmailDto;
import com.samphanie.auiu.common.dto.UpdateUserDto;
import com.samphanie.auiu.common.exception.ApiAsserts;
import com.samphanie.auiu.common.exception.ApiException;
import com.samphanie.auiu.common.utils.ResultCode;
import com.samphanie.auiu.mbg.mapper.UserMapper;
import com.samphanie.auiu.mbg.model.SysRole;
import com.samphanie.auiu.mbg.model.User;
import com.samphanie.auiu.mbg.model.UserRole;
import com.samphanie.auiu.mbg.utils.PageUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author ZSY
 * @email 1451691457@qq.com
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserAdminServiceImpl extends ServiceImpl<UserMapper, User> implements IUserAdminService {

    private final ThreadPoolExecutor executor;

    private final IRoleService roleService;
    private final IUserRoleService userRoleService;
    private final IThirdPartyService thirdPartyService;

    private final PasswordEncoder passwordEncoder;
    private final TotpUtil totpUtil;
    private final CryptoUtil cryptoUtil;

    @Override
    public ClientDetails updateClientDetails(CreateOrUpdateClientDto clientDto) {
        BaseClientDetails client = new BaseClientDetails();
        client.setClientId(clientDto.getClientId());
        client.setScope(clientDto.getScopes());
        client.setAuthorizedGrantTypes(clientDto.getGrantTypes());
        client.setRegisteredRedirectUri(clientDto.getRedirectUris());
        client.setAccessTokenValiditySeconds(clientDto.getAccessTokenValidity());
        client.setRefreshTokenValiditySeconds(clientDto.getRefreshTokenValidity());
        client.setAutoApproveScopes(clientDto.getAutoApproves() != null ? clientDto.getAutoApproves() : new HashSet<>());
        return client;
    }

    @Override
    public PageUtils findAllUser(Map<String, Object> parameters) {
        PageUtils.startPage(parameters);
        val users = list().stream().map(user -> {
                    Set<SysRole> roleList = userRoleService.getRoleList(user.getId());
                    user.setRoles(roleList);

                    return UserUtil.fromUser.apply(user);
                }
        ).collect(Collectors.toList());
        return new PageUtils(users);
    }

    /**
     * 根据用户名查询用户
     *
     * @param username 用户名
     * @return 用户
     */
    public Optional<User> findByUsername(String username) {
        return Optional.ofNullable(
                baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username))
        ).map(user -> {
            Set<SysRole> roleList = userRoleService.getRoleList(user.getId());
            user.setRoles(roleList);
            return user;
        });
    }

    @Override
    public Optional<User> getUserInfoById(Long id) {
        return Optional.ofNullable(
                baseMapper.selectById(id)
        );
    }

    /**
     * 管理员直接创建用户
     *
     * @param createUserDto 用户创建必须的字段
     * @return 创建的用户
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean createUser(CreateUserDto createUserDto) {
        // 生成默认密码
        val password = cryptoUtil.buildDefaultPassword();
        log.info("用户密码: " + password);
        val user = User.builder()
                .username(createUserDto.getUsername())
                .email(createUserDto.getEmail())
                .mobile(createUserDto.getMobile())
                .nickname(createUserDto.getNickname())
                .usingMfa(createUserDto.isUsingMfa())
                .mfaKey(totpUtil.encodeKeyToString())
                .password(passwordEncoder.encode(password))
                .gender(createUserDto.getGender())
                .note(createUserDto.getNote())
                .city(createUserDto.getCity())
                .source("管理员创建")
                .build();

        boolean res = save(user);
        if (res) {
            if (CollUtil.isNotEmpty(createUserDto.getRoles())) {
                List<UserRole> userRoles = createUserDto.getRoles().stream()
                        .map(role -> UserRole.builder()
                                .userId(user.getId())
                                .roleId(role).build())
                        .collect(Collectors.toList());
                userRoleService.saveBatch(userRoles);
            }

            CompletableFuture.runAsync(() -> {
                // 通过email 发送用户密码
                log.info("通过email: " + user.getEmail());
                Map<String, Object> message = Map.of(
                        "username", user.getUsername(),
                        "nickname", user.getNickname(),
                        "password", password,
                        "email", createUserDto.getEmail(),
                        "city", user.getCity(),
                        "createTime", DateUtil.now()
                );
                val sendEmailTo = SendEmailDto.builder()
                        .to(createUserDto.getEmail())
                        .message(message)
                        .subject("注册通知")
                        .template("welcome_user_email")
                        .build();
                thirdPartyService.sendEmail(sendEmailTo);
            }, executor);
        }

        return res;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public User saveOrUpdateUser(User user) {
        val save = saveOrUpdate(user);
        if (!save) {
            ApiAsserts.fail(ResultCode.FAILED);
        }
        return user;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateUser(UpdateUserDto userDto) {
        val userInfo = getUserInfoById(userDto.getId()).orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
        if (CollUtil.isNotEmpty(userDto.getRoles())) {
            // 批量删除用户角色绑定信息
            userRoleService.deleteByUserId(userDto.getId());
            List<UserRole> userRoles = userDto.getRoles().stream().map(item -> UserRole.builder().userId(userDto.getId()).roleId(item).build()).collect(Collectors.toList());
            // 批量绑定用户角色信息
            userRoleService.saveBatch(userRoles);
        }

        userInfo.setNickname(userDto.getNickname());
        userInfo.setEnabled(userDto.isEnabled());
        return updateById(userInfo);
    }

    @Override
    public boolean deleteUser(List<Long> ids) {
        // 删除用户
        val res = removeByIds(ids);
        if (res) {
            CompletableFuture.runAsync(() -> {
                // 级联删除用户绑定的角色信息
                userRoleService.deleteByUserIds(ids);
                // TODO 级联删除用户绑定的第三方用户信息
            }, executor);

        }
        return res;
    }

    @Override
    public User toggleEnabled(String username) {
        return findByUsername(username).map(user -> {
            user.setEnabled(!user.isEnabled());
            return saveOrUpdateUser(user);
        }).orElseThrow();
    }

    /**
     * 查询指定用户的可分配角色列表
     *
     * @param username 用户名
     * @return 用户
     */
    @Override
    public Set<SysRole> findAvailableRolesByUserId(String username) {
        return findByUsername(username)
                .map(user -> {
                    val assignedRoles = user.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.toSet());
                    return roleService.list().stream().filter(role -> !assignedRoles.contains(role.getRoleName())).collect(Collectors.toSet());
                }).orElseThrow();
    }

    @Override
    public void validateUserUniqueFields(String username, String email, String mobile) {

        if (isUsernameExisted(username)) {
            throw new ApiException("用户名已存在");
        }

        if (isEmailExisted(email)) {
            throw new ApiException("电子邮件已存在");
        }
        if (isMobileExisted(mobile)) {
            throw new ApiException("手机号已存在");
        }
    }

    /**
     * 判断用户是否存在
     *
     * @param username
     */
    @Override
    public boolean isUsernameExisted(String username) {
        return baseMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getUsername, username)) > 0;
    }

    @Override
    public boolean isEmailExisted(String email) {
        return baseMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getEmail, email)) > 0;
    }

    @Override
    public boolean isMobileExisted(String mobile) {
        return baseMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getMobile, mobile)) > 0;
    }
}
